package com.project.wxServer.server.pay.impl;

import com.project.common.core.domain.AjaxResult;
import com.project.common.core.redis.RedisCache;
import com.project.common.utils.DateUtils;
import com.project.common.utils.SecurityUtils;
import com.project.wxServer.constant.WXConfig;
import com.project.wxServer.domain.pay.FaceToFace;
import com.project.wxServer.domain.pay.TOrderRecord;
import com.project.wxServer.domain.pay.WxPayInfo;
import com.project.wxServer.enums.CommodityType;
import com.project.wxServer.server.login.ITOrderRecordService;
import com.project.wxServer.server.pay.IPayService;
import com.project.wxServer.server.wechat.WechatRefundApiResult;
import com.project.wxServer.server.wechat.WechatServer;
import com.project.wxServer.utils.CharUtil;
import com.project.wxServer.utils.CommonUtil;
import com.project.wxServer.utils.MapUtil;
import com.project.wxServer.utils.XmlUtil;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import javax.annotation.Resource;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.ByteArrayOutputStream;
import java.io.InputStream;
import java.math.BigDecimal;
import java.util.Map;
import java.util.Scanner;
import java.util.TreeMap;
import java.util.concurrent.ConcurrentHashMap;

/**
 * description：微信支付
 *
 * @author kang
 * @since 2021/6/29　19:49
 */
@Service
public class PayServiceImpl implements IPayService {

    private static final Logger logger = LoggerFactory.getLogger(PayServiceImpl.class);

    @Resource
    private WXConfig wxConfig;

    @Resource
    private RedisCache redisCache;

    @Resource
    private WechatServer wechatServer;

    @Resource
    private ITOrderRecordService itOrderRecordService;

    //缓存
    private static ConcurrentHashMap<Object, Map<Object, Object>> cacheHashMap = new ConcurrentHashMap<>();

    /**
     * 微信支付接口
     *
     * @param wxPayInfo 支付对象
     * @return
     */
    @Override
    @Transactional
    public synchronized AjaxResult payPrepay(WxPayInfo wxPayInfo, HttpServletRequest request) {
        String openid = SecurityUtils.getLoginUser().getUser().getOpenId();
        String nonceStr = CharUtil.getRandomString(32);
        String out_trade_no = CommonUtil.generateOrderNumber();
        Map<Object, Object> resultObj = new TreeMap();
        TOrderRecord wxOrder = null;
        try {
            Map<Object, Object> parame = new TreeMap<Object, Object>();
            parame.put("appid", wxConfig.getAppId());
            // 商家账号。
            parame.put("mch_id", wxConfig.getMchId());
            String randomStr = CharUtil.getRandomNum(18).toUpperCase();
            // 随机字符串
            parame.put("nonce_str", randomStr);
            // 商户订单编号
            parame.put("out_trade_no", out_trade_no);
            // 商品描述
            parame.put("body", "微信支付");
            //支付金额
            BigDecimal fakeMoney = new BigDecimal(0.01);
            parame.put("total_fee", fakeMoney.multiply(new BigDecimal(100)).intValue());
            // 回调地址
            parame.put("notify_url", wxConfig.getNotifyUrl());
            // 交易类型APP
            parame.put("trade_type", wxConfig.getTradeType());
            parame.put("spbill_create_ip", getClientIp(request));
            parame.put("openid", openid);
            String sign = wechatServer.arraySign(parame, wxConfig.getPaySignKey());
            // 数字签证
            parame.put("sign", sign);
            String xml = MapUtil.convertMap2Xml(parame);
            logger.info("xml:" + xml);
            //生成二维码
            Map<String, Object> resultUn = XmlUtil.xmlStrToMap(wechatServer.requestOnce(wxConfig.getUniformorder(), xml));
            // 响应报文
            String return_code = MapUtil.getString("return_code", resultUn);
            String return_msg = MapUtil.getString("return_msg", resultUn);
            if (return_code.equalsIgnoreCase("FAIL")) {
                return AjaxResult.error("支付失败", return_msg);
            } else if (return_code.equalsIgnoreCase("SUCCESS")) {
                // 返回数据
                String result_code = MapUtil.getString("result_code", resultUn);
                String err_code_des = MapUtil.getString("err_code_des", resultUn);
                if (result_code.equalsIgnoreCase("FAIL")) {
                    return AjaxResult.error("支付失败", err_code_des);
                } else if (result_code.equalsIgnoreCase("SUCCESS")) {
                    String prepay_id = MapUtil.getString("prepay_id", resultUn);
                    parame.put("prepay_id", prepay_id);
                    // 先生成paySign 参考https://pay.weixin.qq.com/wiki/doc/api/wxa/wxa_api.php?chapter=7_7&index=5
                    resultObj.put("appId", wxConfig.getAppId());
                    resultObj.put("timeStamp", DateUtils.getTime());
                    resultObj.put("nonceStr", nonceStr);
                    resultObj.put("package", "prepay_id=" + prepay_id);
                    resultObj.put("signType", "MD5");
                    String paySign = wechatServer.arraySign(resultObj, wxConfig.getPaySignKey());
                    resultObj.put("paySign", paySign);
                    resultObj.put("out_trade_no", out_trade_no);
                    /*//创建虚拟订单（存储数据）
                    parame.put("wx_pay_info", wxPayInfo);
                    cacheHashMap.put(out_trade_no, parame);*/
                    wxPayInfo.setPayType(CommodityType.ON_PAYMENT.getValue());
                    parame.put("wx_pay_info", wxPayInfo);
                    wxOrder = itOrderRecordService.createWXOrder(parame);
                    logger.info("{} - 微信支付下单成功", out_trade_no);
                    return AjaxResult.success("下单成功", resultObj);
                }
            }
        } catch (Exception e) {
            logger.error("支付失败 - {}", e.getMessage());
            return AjaxResult.error("支付失败", e.getMessage());
        }
        logger.error("{} - 微信支付下单失败", wxPayInfo.getUserId());
        return AjaxResult.error("支付失败");
    }

    /**
     * 微信订单回调接口
     */
    @Override
    public synchronized void wxNotify(HttpServletRequest request, HttpServletResponse response) {
        WechatRefundApiResult result = null;
        String result_code = null;
        String out_trade_no = null;
        try {
            request.setCharacterEncoding("UTF-8");
            InputStream in = request.getInputStream();
            ByteArrayOutputStream out = new ByteArrayOutputStream();
            byte[] buffer = new byte[1024];
            int len = 0;
            while ((len = in.read(buffer)) != -1) {
                out.write(buffer, 0, len);
            }
            out.close();
            in.close();
            //xml数据
            String reponseXml = new String(out.toByteArray(), "utf-8");
            result = (WechatRefundApiResult) XmlUtil.xmlStrToBean(reponseXml, WechatRefundApiResult.class);
            result_code = result.getResult_code();
            //创建订单
            out_trade_no = result.getOut_trade_no();
            if (result_code.equalsIgnoreCase("FAIL")) {
                logger.error("订单" + out_trade_no + "支付失败");
                //更新数据库订单状态
                itOrderRecordService.updateWXOrder("0", out_trade_no);
                //支付成功则把该键存入缓存
                redisCache.setCacheObject(out_trade_no, result_code);
            } else if (result_code.equalsIgnoreCase("SUCCESS")) {
                logger.info("订单" + out_trade_no + "支付成功");
                //更新数据库订单状态
                itOrderRecordService.updateWXOrder("1", out_trade_no);
                //支付成功则把该键存入缓存
                redisCache.setCacheObject(out_trade_no, result_code);
            }
        } catch (Exception e) {
            logger.error(e.getMessage());
        }
    }

    /**
     * 微信支付结果查询
     */
    public String getPayResult(WxPayInfo wxPayInfo) {
        String outTradeNo = wxPayInfo.getOutTradeNo();
        return redisCache.getCacheObject(outTradeNo.toString());
    }

    /**
     * 获取请求方IP
     *
     * @return 客户端Ip
     */
    public String getClientIp(HttpServletRequest request) {
        String xff = request.getHeader("X-Real-IP");
        if (xff != null) {
            return xff;
        }
        xff = request.getHeader("x-forwarded-for");
        if (xff == null) {
            return "8.8.8.8";
        }
        return xff;
    }

    //返回微信服务
    public static String setXml(String return_code, String return_msg) {
        return "<xml><return_code><![CDATA[" + return_code + "]]></return_code><return_msg><![CDATA[" + return_msg + "]]></return_msg></xml>";
    }

    /**
     * 获取微信支付的二维码地址
     *
     * @return
     * @throws Exception
     * @author chenp
     */
    @Override
    public AjaxResult getCodeUrl(FaceToFace faceToFace, HttpServletRequest request) {
        String urlCode = null;
        String nonceStr = CharUtil.getRandomString(32);
        String out_trade_no = CommonUtil.generateOrderNumber();
        Map<Object, Object> resultObj = new TreeMap();
        try {
            Map<Object, Object> parame = new TreeMap<Object, Object>();
            parame.put("appid", wxConfig.getAppId());
            // 商家账号。
            parame.put("mch_id", wxConfig.getMchId());
            String randomStr = CharUtil.getRandomNum(18).toUpperCase();
            // 随机字符串
            parame.put("nonce_str", randomStr);
            // 商户订单编号
            parame.put("out_trade_no", out_trade_no);
            // 商品描述
            parame.put("body", "微信扫码支付");
            //支付金额
            BigDecimal fakeMoney = new BigDecimal(0.01);
            parame.put("total_fee", fakeMoney.multiply(new BigDecimal(100)).intValue());
            // 回调地址
            parame.put("notify_url", wxConfig.getNotifyUrl());
            // 交易类型APP
            parame.put("trade_type", wxConfig.getTradeTypeNative());
            parame.put("spbill_create_ip", getClientIp(request));
            String sign = wechatServer.arraySign(parame, wxConfig.getPaySignKey());
            // 数字签证
            parame.put("sign", sign);
            String xml = MapUtil.convertMap2Xml(parame);
            logger.info("xml:" + xml);
            //生成二维码
            Map<String, Object> resultUn = XmlUtil.xmlStrToMap(wechatServer.requestOnce(wxConfig.getUniformorder(), xml));
            logger.info("二维码" + resultUn);
            urlCode = (String) resultUn.get("code_url");
            // 响应报文
            String return_code = MapUtil.getString("return_code", resultUn);
            String return_msg = MapUtil.getString("return_msg", resultUn);
            if (return_code.equalsIgnoreCase("FAIL")) {
                return AjaxResult.error("二维码生成失败", return_msg);
            } else if (return_code.equalsIgnoreCase("SUCCESS")) {
                // 返回数据
                String result_code = MapUtil.getString("result_code", resultUn);
                String err_code_des = MapUtil.getString("err_code_des", resultUn);
                if (result_code.equalsIgnoreCase("FAIL")) {
                    return AjaxResult.error("二维码生成失败", err_code_des);
                } else if (result_code.equalsIgnoreCase("SUCCESS")) {
                    String prepay_id = MapUtil.getString("prepay_id", resultUn);
                    parame.put("prepay_id", prepay_id);
                    // 先生成paySign 参考https://pay.weixin.qq.com/wiki/doc/api/wxa/wxa_api.php?chapter=7_7&index=5
                    resultObj.put("appId", wxConfig.getAppId());
                    resultObj.put("timeStamp", DateUtils.getTime());
                    resultObj.put("nonceStr", nonceStr);
                    resultObj.put("package", "prepay_id=" + prepay_id);
                    resultObj.put("signType", "MD5");
                    resultObj.put("code_url", urlCode);
                    String paySign = wechatServer.arraySign(resultObj, wxConfig.getPaySignKey());
                    resultObj.put("paySign", paySign);
                    resultObj.put("out_trade_no", out_trade_no);
                    //创建虚拟订单（存储数据）
                    parame.put("Face_To_Face", faceToFace);
                    cacheHashMap.put(out_trade_no, parame);
                    logger.info("{} - 微信扫码支付成功", out_trade_no);
                    return AjaxResult.success("二维码生成成功", resultObj);
                }
            }
        } catch (Exception e) {
            logger.error("二维码生成失败", e.getMessage());
            e.printStackTrace();
        }
        return AjaxResult.error("二维码生成失败");
    }
}

